package com.xuan.xwaterfall;

import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.ScrollView;

/**
 * 一个添加了回收不显示View方法的瀑布流实现,并可设置手指滑动正反加载方向上缓冲的View的数量
 * 版本一,已具基本功能,但插入,删除操作调用getView过多
 * 
 * @author xuan
 * 
 */
public abstract class XWaterFall extends ScrollView {
	private String TAG = "WaterFall";

	private LinearLayout llContent, llRoot, svChild;
	private View headView, footView;
	private Context context;
	private LayoutInflater inflater;
	// 手机的宽度
	int width;
	// 显示的列数
	private int columnCount = 0;
	// 一次加载的个数
	private int oneShow;
	// 正反方向缓存的View的数量
	private int positionCacheNum, oppositeCacheNum;
	// N列LinearLayout
	private LinearLayout[] columnArr;
	// 每一列的View的数量
	private int[] columnChildCount;
	// 记录每一列的总高度
	private long[] heightArr;
	// 记录View的平均高度
	private int avgHeight;
	// 记录每一列View的顶点Top
	private Map<Integer, Integer>[] maTop;
	// 所有View的相关信息
	private Map<Integer, ViewItem> viewItems = new HashMap<Integer, ViewItem>();
	// 当前显示的View中最顶端的View
	private int curTopView = 0;
	// 加载的View的个数
	private int viewCount;
	// 回收与不回收的View的position
	private RecyclePosition recycle = new RecyclePosition();
	// 显示的View的position
	int showStart, showEnd;

	private boolean isInit;
	private boolean isScrollBottom;
	private boolean isToBigger;
	private boolean isGetHeightWork;
	// 测滑动速度
	VelocityTracker vTracker;
	// 上一次滑动的位置
	int lastScrollY;

	/**
	 * 
	 * @param columnCount
	 *            设置waterfall列数
	 * @param waterFallWidth
	 *            waterfall的宽度,不合法值时,采用手机宽度
	 */
	public XWaterFall(Context context,int columnCount, int waterFallWidth) {
		// TODO Auto-generated constructor stub
		super(context);
		setup(context);
		init(columnCount, waterFallWidth);
	}

	private void setup(Context context) {
		inflater = LayoutInflater.from(context);
		this.context = context;
		this.oneShow = getNewChildNum();
		svChild = new LinearLayout(context);
		LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
				LinearLayout.LayoutParams.FILL_PARENT,
				LinearLayout.LayoutParams.WRAP_CONTENT);
		svChild.setOrientation(LinearLayout.VERTICAL);
		llContent = new LinearLayout(context);
		llContent.setOrientation(LinearLayout.HORIZONTAL);
		svChild.addView(llContent, params);
		this.addView(svChild, params);
	}

	// 不适合一次加载过多
	public void notifyDataSetChanged() {
		View child;
		boolean isChange = false;
		int childCount = getContentChildCount();
		int curCount = viewCount;
		// 检查目前的View
		for (int i = 0; i < viewCount && i < childCount; i++) {
			ViewItem item = viewItems.get(i);
			if (item.mark != getMark(i)) {// 更新View
				refreshView(item);
				isChange = true;
			}
		}
		// 将多出来的View删除
		if (viewCount > childCount) {
			int i = childCount - 1 < 0 ? 0 : childCount - 1;
			for (; i < curCount; i++) {
				deleteView(viewItems.get(i));
			}
		}
		if (isChange) {
			refreshViewTop();
			initRecyclePosition(recycle, showStart, showEnd, true);
		}
		// 添加oneShow数量的View
		if (getChildHeight(0) > 0) {
			isGetHeightWork = true;
		}else{
			refreshHeight();
		}
		int upPosition = viewCount + oneShow;
		for (int i = viewCount; i < upPosition && i < childCount; i++) {
			child = getView(i);
			addColumnView(child, i, getChildHeight(i));
		}
		if (isInit) {
			int initShowNum = 4 * getColumnCount() < oneShow ? 4 * getColumnCount()
					: oneShow;
			for (int i = 0; i < initShowNum; i++) {
				onResumeView(i, viewItems.get(i).view);
			}
			initRecyclePosition(recycle, 0, initShowNum - 1, true);
			isInit = false;
		} else {
			if (isScrollBottom) {
				int addPosition = positionCacheNum + recycle.notRecycleEnd < viewItems
						.size() ? positionCacheNum + recycle.notRecycleEnd
						: viewItems.size() - 1;
				Log.v(TAG, "isScrollBottom : " + addPosition);
				RecyclePosition newRecycle = new RecyclePosition();
				initRecyclePosition(newRecycle, showStart, showEnd, true);
				possessRecycle(recycle, newRecycle);
				recycle = newRecycle;
				for (int i = recycle.notRecycleEnd + 1; i <= addPosition; i++) {
					onResumeView(i, viewItems.get(i).view);
				}
				recycle.notRecycleEnd = addPosition;
			}
		}
	}

	/**
	 * 如果View改变了将调用改方法改变View
	 * 
	 * @param item
	 */
	private void refreshView(ViewItem item) {
		Log.v(TAG, "refreshView position: " + item.position);
		View view = getView(item.position);
		if (item.position >= recycle.notRecycleStart
				&& item.position <= recycle.notRecycleEnd) {
			onResumeView(item.position, view);
		}
		columnArr[item.column].removeViewAt(item.columnPosition);
		columnArr[item.column].addView(view, item.columnPosition);

		item.view = view;
		item.mark = getMark(item.position);
		item.top = 0;
	}

	/**
	 * 更新每列的高度和平均高度
	 */
	private void refreshHeight() {
		View child;
		long sum = 0;
		if (!isInit) {
			for (int i = 0; i < columnCount; i++) {
				child = columnArr[i]
						.getChildAt(columnArr[i].getChildCount() - 1);
				heightArr[i] = child.getTop() + child.getHeight();
				Log.v(TAG, "列" + i + "高度:" + heightArr[i]);
				sum += heightArr[i];
			}
			avgHeight = (int) (sum / viewCount);
			if(avgHeight <= 0 ){
				avgHeight = 1;
			}
		} else {
			avgHeight = 1;
		}
		Log.v(TAG, "平均高度" + avgHeight);
	}

	/**
	 * 添加View
	 * 
	 * @param view
	 * @param position
	 * @param height
	 */
	private void addColumnView(View view, int position, int height) {
		int minHeightColumn = 0;
		for (int i = 1; i < columnCount; i++) {
			if (heightArr[i] < heightArr[minHeightColumn]) {
				minHeightColumn = i;
			}
		}
		columnArr[minHeightColumn].addView(view);
		viewCount++;
		if (isGetHeightWork) {
			heightArr[minHeightColumn] += height;
		} else {
			heightArr[minHeightColumn] += avgHeight;
		}
		columnChildCount[minHeightColumn]++;

		ViewItem item = new ViewItem();
		item.view = view;
		item.position = position;
		item.columnPosition = columnChildCount[minHeightColumn] - 1;
		item.mark = getMark(position);
		item.column = minHeightColumn;
		viewItems.put(position, item);
	}

	private void deleteView(ViewItem item) {
		columnArr[item.column].removeView(item.view);
		viewCount--;
		columnChildCount[item.column]--;
		heightArr[item.column] -= getChildHeight(item.position);
		viewItems.remove(item.position);
	}

	/**
	 * 
	 * @param columnCount
	 *            设置waterfall列数
	 * @param waterFallWidth
	 *            waterfall的宽度,不合法值时,采用手机宽度
	 */
	private void init(int columnCount, int waterFallWidth) {
		isInit = true;
		if (waterFallWidth <= 0) {
			WindowManager wm = ((Activity) context).getWindowManager();
			width = wm.getDefaultDisplay().getWidth();
		} else {
			width = waterFallWidth;
		}
		if (columnCount > 0) {
			columnArr = new LinearLayout[columnCount];
			columnChildCount = new int[columnCount];
			heightArr = new long[columnCount];
			maTop = new HashMap[columnCount];
			this.columnCount = columnCount;
			positionCacheNum = columnCount * 6;
			oppositeCacheNum = columnCount * 3;
			for (int i = 0; i < columnCount; i++) {
				LinearLayout llColumn = new LinearLayout(context);
				int columnWidth = width / columnCount;
				LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
						columnWidth, LinearLayout.LayoutParams.WRAP_CONTENT);
				llColumn.setOrientation(LinearLayout.VERTICAL);
				llContent.addView(llColumn, params);
				columnArr[i] = llColumn;
				heightArr[i] = 0;
				maTop[i] = new HashMap<Integer, Integer>();
			}

		}
	}

	// 返回列数
	public int getColumnCount() {
		return columnCount;
	}

	// 在顶部添加一个View
	public final void addHeadView(View view) {
		svChild.addView(view, 0);
	}

	// 在底部添加一个View
	public final void addFootView(View view) {
		svChild.addView(view);
	}

	// 更新目前的View的top
	private void initViewTop() {
		ViewItem item;
		for (int i = 0; i < viewCount; i++) {
			if (viewItems.get(i).top == 0) {
				item = viewItems.get(i);
				item.top = item.view.getTop();
			}
		}
	}

	// 更新所有View的top
	private void refreshViewTop() {
		ViewItem item;
		for (int i = 0; i < viewCount; i++) {
			item = viewItems.get(i);
			item.top = item.view.getTop();
		}
	}
	

	/**
	 * 回收和恢复View,任何View回收与回复会只会交替进行
	 * 
	 * @param t
	 */
	private void recycleAndResumeView(int top, boolean isToBigger) {
		Log.v(TAG, "recycleAndResumeView" + "top:" + top + "变大 :" + isToBigger);
		RecyclePosition newRecycle = new RecyclePosition();
		initViewTop();

		ViewItem item;
		for (int i = showStart; i >= 0 && i < viewCount;) {
			item = viewItems.get(i);
			if (isToBigger) {// 手指向上滑动
				if (item.top <= top) {
					i++;
				} else {// 目前的View的top大于显示屏顶端
					showStart = i - getColumnCount();
					break;
				}
			} else {// 手指向下滑动
				if (item.top <= top) {
					showStart = i - getColumnCount() + 1;
					break;
				} else {
					i--;
				}
			}
		}
		if (showStart < 0)
			showStart = 0;
		int endTop = getHeight() + top;
		showEnd = viewCount - 1;
		for (int i = showStart; i < viewCount; i++) {
			item = viewItems.get(i);
			if (item.top > endTop) {
				showEnd = i - 1;
				break;
			}
		}
		initRecyclePosition(newRecycle, showStart, showEnd, isToBigger);
		possessRecycle(recycle, newRecycle);
		recycle = newRecycle;
		Log.v(TAG, "回收结束:" + "top:" + getScrollY() + "变大 :" + isToBigger);
	}

	/**
	 * 回收View和恢复View
	 * 
	 * @param old
	 * @param r
	 */
	private void possessRecycle(RecyclePosition old, RecyclePosition r) {
		int i = r.notRecycleStart;
		while (i < old.notRecycleStart) {
			onResumeView(i, viewItems.get(i).view);
			i++;
		}
		i = old.notRecycleEnd + 1;
		while (i <= r.notRecycleEnd) {
			onResumeView(i, viewItems.get(i).view);
			i++;
		}
		// 回收上部分
		i = old.upRecycleEnd + 1;
		while (i <= r.upRecycleEnd) {
			onRecycleView(i, viewItems.get(i).view);
			i++;
		}
		// 回收下部分
		i = r.downRecycleStart;
		while (i < old.downRecycleStart) {
			onRecycleView(i, viewItems.get(i).view);
			i++;
		}
		// 回收新添加部分
		i = r.downRecycleEnd + 1;
		while (i < r.downRecycleEnd && i < viewCount) {
			onRecycleView(i, viewItems.get(i).view);
			i++;
		}

	}

	private void initRecyclePosition(RecyclePosition r, int showStart,
			int showEnd, boolean isToBigger) {
		Log.v(TAG, "显示: " + showStart + " ~ " + showEnd);
		if (isToBigger) {
			r.notRecycleStart = showStart - oppositeCacheNum > 0 ? showStart
					- oppositeCacheNum : 0;
			r.notRecycleEnd = showEnd + positionCacheNum < viewCount ? showEnd
					+ positionCacheNum : viewCount - 1;
		} else {
			r.notRecycleStart = showStart - positionCacheNum > 0 ? showStart
					- positionCacheNum : 0;
			r.notRecycleEnd = showEnd + oppositeCacheNum < viewCount ? showEnd
					+ oppositeCacheNum : viewCount - 1;
		}
		r.upRecycleEnd = r.notRecycleStart - 1;

		r.downRecycleStart = r.notRecycleEnd + 1;
		r.downRecycleEnd = viewCount - 1;

		Log.v(TAG, "不回收:" + r.notRecycleStart + " ~ " + r.notRecycleEnd);
		Log.v(TAG, "回收小于:" + r.upRecycleEnd + " 大于 " + r.downRecycleStart
				+ "的View");
	}

	Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			int top = getScrollY();
			if (lastScrollY == top) {
				recycleAndResumeView(top, isToBigger);
				// 滑到底部
				if (isScrollToBottom()) {
					onScrollToBottom();
				}
			} else {
				lastScrollY = getScrollY();
				handler.sendEmptyMessageDelayed(0, 100);
			}
		}
	};

	private boolean isFirstTouch;

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:// 上层View使用onClickListener时将不会有ACTION_DOWN
			Log.v(TAG, "ACTION_DOWN ");
			break;
		case MotionEvent.ACTION_MOVE:
			if (!isFirstTouch || vTracker == null) {
				isFirstTouch = true;
				vTracker = VelocityTracker.obtain();
			}
			vTracker.addMovement(ev);
			break;
		case MotionEvent.ACTION_UP:// 停止滚动后开始回收和恢复
			vTracker.addMovement(ev);
			vTracker.computeCurrentVelocity(1000);
			float speed = vTracker.getYVelocity();
			Log.v(TAG, "ACTION_UP速度 " + speed);
			if (speed < 0) {
				isToBigger = true;
			} else {
				isToBigger = false;
			}
			lastScrollY = getScrollY();
			if (Math.abs(speed) > 100) {
				handler.sendEmptyMessageDelayed(0, 100);
			} else {
				recycleAndResumeView(lastScrollY, isToBigger);
				if (isScrollToBottom()) {
					onScrollToBottom();
				}
			}
			vTracker.recycle();
			isFirstTouch = false;
			break;
		}
		return super.onTouchEvent(ev);
	}


	public boolean isScrollToBottom() {
		if (!isScrollBottom
				&& svChild.getMeasuredHeight() <= getScrollY() + getHeight()) {
			isScrollBottom = true;
		} else {
			isScrollBottom = false;
		}
		return isScrollBottom;
	}

	public void onScrollToBottom() {

	}

	@Override
	protected void onFinishInflate() {
		// TODO Auto-generated method stub
		Log.v(TAG, "height " + getHeight() + "width" + getWidth());
		super.onFinishInflate();
	}

	/**
	 * notifyDataSetChange时调用来获取子View
	 * 
	 * @param position
	 * @return 返回需要添加的View
	 */
	public abstract View getView(int position);

	/**
	 * 如果能确定对应的View的高度则可在子类覆盖该方法
	 * 
	 * @param position
	 * @return
	 */
	public abstract int getChildHeight(int position);

	/**
	 * 返回position对应的View的标识,用以判断View是否需要更新
	 * 
	 * @param position
	 * @return
	 */
	public abstract Object getMark(int position);

	/**
	 * 获得WaterFall中子View的数量
	 * 
	 * @return
	 */
	public abstract int getContentChildCount();

	/**
	 * 一次notifyDataSetChanged()最多添加的View的个数
	 * 
	 * @return
	 */
	public abstract int getNewChildNum();

	/**
	 * 有新超出显示屏的View时调用该方法
	 * 
	 * @param position
	 * @param view
	 */
	protected abstract void onRecycleView(int position, View view);

	/**
	 * 有新进入显示屏的View时调用该方法
	 * 
	 * @param position
	 * @param view
	 */
	protected abstract void onResumeView(int position, View view);

	/**
	 * 
	 * 设置显示屏中加载正方向上缓存的View的个数
	 * 
	 * @param num
	 */
	public void setPositionCacheNum(int num) {
		positionCacheNum = num;
	}

	/**
	 * 
	 * 设置显示屏中加载反方向上缓存的View的个数
	 * 
	 * @param num
	 */
	public void setOppositeCacheNum(int num) {
		oppositeCacheNum = num;
	}

	class ViewItem {
		int position;
		View view; // View
		Object mark; // 标识
		int top;
		int column; // 显示的列
		int columnPosition; // 在列中对应的position
	}

	class RecyclePosition {
		// 上半部分回收的View
		int upRecycleEnd;
		// 下半部分回收的View
		int downRecycleStart;
		int downRecycleEnd;
		// 不回收部分
		int notRecycleStart;
		int notRecycleEnd;
	}
}
